#include "../../pch.h"
#include "REFNameTable.h"
#include "REFXMLNamespaceManager.h"

namespace	BFX
{

void REFXMLNamespaceManager::NamespaceDeclaration::Set(const UniString& strPrefix, const UniString& strUri, int nScopeId, int nPreviousNsIndex)
{
	m_strPrefix	= strPrefix;
	m_strUri	= strUri;
	m_nScopeId	= nScopeId;
	m_nPreviousNsIndex	= nPreviousNsIndex;
}

// Initializes a new instance with the specified XmlNameTable.

REFXMLNamespaceManager::REFXMLNamespaceManager(REFNameTable* pNameTable)
{
	m_pNameTable = pNameTable;
	m_pNameTable->AddRef();

	m_strXml = m_pNameTable->Add(USTR("xml"));
	m_strXmlNs = m_pNameTable->Add(USTR("xmlns"));

	m_nNsDeclsLength	= 16;
	m_pNsDecls = new NamespaceDeclaration[m_nNsDeclsLength];
	UniString strEmpty = m_pNameTable->Add(UniString());
	m_pNsDecls[0].Set(strEmpty, strEmpty, -1, -1);
	m_pNsDecls[1].Set(m_strXmlNs, m_pNameTable->Add(USTR("http://www.w3.org/2000/xmlns/")), -1, -1);
	m_pNsDecls[2].Set(m_strXml, m_pNameTable->Add(USTR("http://www.w3.org/XML/1998/namespace")), 0, -1);
	m_nLastDecl	= 2;
	m_nScopeId	= 1;
}

REFXMLNamespaceManager::~REFXMLNamespaceManager()
{
	m_pNameTable->Release();
	delete[] m_pNsDecls;
}

// Return the namespace to which the specified prefix is mapped.  Returns null if the prefix isn't mapped to
// a namespace.  
// The "xml" prefix is always mapped to the "http://www.w3.org/XML/1998/namespace" namespace.
// The "xmlns" prefix is always mapped to the "http://www.w3.org/2000/xmlns/" namespace.
// If the default namespace has not been defined, then the "" prefix is mapped to "" (the empty namespace).
UniString REFXMLNamespaceManager::LookupNamespace(const UniString& strPrefix) const
{
	int nDeclIndex = LookupNamespaceDecl(strPrefix);
	return (nDeclIndex == -1) ? UniString() : m_pNsDecls[nDeclIndex].m_strUri;
}

// Return a prefix which is mapped to the specified namespace.  Multiple prefixes can be mapped to the
// same namespace, and it is undefined which prefix will be returned.  Returns null if no prefixes are
// mapped to the namespace.  
// The "xml" prefix is always mapped to the "http://www.w3.org/XML/1998/namespace" namespace.
// The "xmlns" prefix is always mapped to the "http://www.w3.org/2000/xmlns/" namespace.
// If the default namespace has not been defined, then the "" prefix is mapped to "" (the empty namespace).
UniString REFXMLNamespaceManager::LookupPrefix(const UniString& strNamespaceName) const
{
	for(int nThisDecl = m_nLastDecl; nThisDecl >= 0; nThisDecl --)
	{
		if (m_pNsDecls[nThisDecl].m_strUri == strNamespaceName)
		{
			UniString strPrefix = m_pNsDecls[nThisDecl].m_strPrefix;
			if (LookupNamespace(strPrefix) == strNamespaceName)
			{
				return strPrefix;
			}
		}
	}
	return UniString();
}

// Gets a value indicating whether the supplied prefix has a namespace defined for the current pushed scope.
// Returns true if there is a namespace defined; otherwise, false.
bool REFXMLNamespaceManager::HasNamespace(const UniString& strPrefix) const
{
	for (int nThisDecl = m_nLastDecl; m_pNsDecls[nThisDecl].m_nScopeId == m_nScopeId; nThisDecl--)
	{
		if (m_pNsDecls[nThisDecl].m_strPrefix == strPrefix && !m_pNsDecls[nThisDecl].m_strUri.IsEmpty())
		{
			if (!strPrefix.IsEmpty() || !m_pNsDecls[nThisDecl].m_strUri.IsEmpty())
			{
				return true;
			}
			return false;
		}
	}
	return false;
}
// Adds the given namespace to the collection.
// Return true if success; or false if reserved names specified.
bool REFXMLNamespaceManager::AddNamespace(const UniString& strPrefix, const UniString& strUri)
{
	if (m_strXml == strPrefix && strUri != USTR("http://www.w3.org/XML/1998/namespace"))
	{
		return false;
	}
	if (m_strXmlNs == strPrefix)
	{
		return false;
	}

	int nDeclIndex = LookupNamespaceDecl(strPrefix);
	int nPreviousDeclIndex = -1;
	if (nDeclIndex != -1)
	{
		if (m_pNsDecls[nDeclIndex].m_nScopeId == m_nScopeId)
		{
			// redefine if in the same scope
			m_pNsDecls[nDeclIndex].m_strUri = strUri;
			return true;
		}
		else
		{
			// othewise link
			nPreviousDeclIndex = nDeclIndex;
		}
	}

	// set new namespace declaration
	if (m_nLastDecl == m_nNsDeclsLength - 1)
	{
		NamespaceDeclaration* pNewNsDecls = new NamespaceDeclaration[m_nNsDeclsLength * 2];
		CopyObjects(pNewNsDecls, m_pNsDecls, m_nNsDeclsLength);
		delete[] m_pNsDecls;
		m_pNsDecls = pNewNsDecls;
	}
	m_pNsDecls[++m_nLastDecl].Set(strPrefix, strUri, m_nScopeId, nPreviousDeclIndex);

	return true;
}
// Removes the given namespace for the given prefix.
void REFXMLNamespaceManager::RemoveNamespace(const UniString& strPrefix, const UniString& strUri)
{
	int nDeclIndex = LookupNamespaceDecl(strPrefix);
	while (nDeclIndex != -1)
	{
		if (m_pNsDecls[nDeclIndex].m_strUri == strUri && m_pNsDecls[nDeclIndex].m_nScopeId == m_nScopeId)
		{
			m_pNsDecls[nDeclIndex].m_strUri.SetEmpty();
		}
		nDeclIndex = m_pNsDecls[nDeclIndex].m_nPreviousNsIndex;
	}
}

// Pushes a namespace scope onto the stack.
void REFXMLNamespaceManager::PushScope()
{
	m_nScopeId++;
}
// Pops a namespace scope off the stack.
// Returns true if there are namespace scopes left on the stack; false if there are no more namespaces to pop.
bool REFXMLNamespaceManager::PopScope()
{
	int nDecl = m_nLastDecl;
	if (m_nScopeId == 1)
	{
		return false;
	}
	while(m_pNsDecls[nDecl].m_nScopeId == m_nScopeId)
	{
		nDecl--;
		BFX_ASSERT(nDecl >= 2);
	}
	m_nLastDecl = nDecl;
	m_nScopeId--;
	return true;
}

int REFXMLNamespaceManager::LookupNamespaceDecl(const UniString& strPrefix) const
{
	// NOTE: we can use hashtable for faster lookup when there is lots of namespaces.
	for(int nThisDecl = m_nLastDecl; nThisDecl >= 0; nThisDecl --)
	{
		if (m_pNsDecls[nThisDecl].m_strPrefix == strPrefix && !m_pNsDecls[nThisDecl].m_strUri.IsEmpty())
		{
			return nThisDecl;
		}
	}
	return -1;
}

}	//	namespace	BFX
